<?php
/*
Plugin Name: Total Slider
Plugin URI: http://www.totalslider.com/
Description: The best experience for building sliders, with true WYSIWYG, drag & drop and more!
Version: 1.0.1
Author: Peter Upfold
Author URI: http://www.vanpattenmedia.com/
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: total_slider
/* ----------------------------------------------*/

/*  Copyright (C) 2011-2012 Peter Upfold.

    This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/


define('TOTAL_SLIDER_IN_FUNCTIONS', true);
define('TOTAL_SLIDER_REQUIRED_CAPABILITY', 'total_slider_manage_slides');
define('TOTAL_SLIDER_MAX_SLIDE_GROUPS', 24);
define('TOTAL_SLIDER_DEFAULT_CROP_WIDTH', 600);
define('TOTAL_SLIDER_DEFAULT_CROP_HEIGHT', 300);


require_once(dirname(__FILE__).'/slides_backend.php');

/******************************************** Total_Slider main class ********************************************/

class Total_Slider {


	/* data structure

		a serialized array stored as a wp_option


		total_slider_slides_[slug]

			[0]
				id				[string] (generated by str_replace('.', '_', uniqid('', true)); )
				title			[string]
				description		[string]
				background		[string]
				link			[string]
				title_pos_x		[int]
				title_pos_y		[int]

			[1]
				id				[string] (generated by str_replace('.', '_', uniqid('', true)); )
				title			[string]
				description		[string]
				background		[string]
				link			[string]
				title_pos_x		[int]
				title_pos_y		[int]

			[2] ...

	*/

	/***********	Registration, first-time, etc.	***********/

	public function createSlidesOptionField() {
	/*
		Upon plugin activation, creates the total_slider_slide_groups option
		in wp_options, if it does not already exist.

		Also set up a base capability for administrator to access the Slider
		Admin page, and configure some default general options.
	*/

		if (!get_option('total_slider_slide_groups')) {

			add_option('total_slider_slide_groups', array()); // create with a blank array

		}

		// set the capability for administrator so they can visit the options page
		$admin = get_role('administrator');
		$admin->add_cap(TOTAL_SLIDER_REQUIRED_CAPABILITY);

		// set up default general options

		if (!get_option('total_slider_general_options'))
		{
			add_option('total_slider_general_options', array(
				'should_enqueue_template'	=> 	'1'
			));
		}

	}

	public function registerAsWidget() {
	/*
		Register the output to the theme as a widget
	*/

		register_widget('Total_Slider_Widget');

	}

	public function loadTextDomain()
	{
	/*
		Load the GetText domain for this plugin's translatable strings.
	*/

		load_plugin_textdomain( 'total_slider', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );

	}

	/***********	Utility Functions	***********/

	public function sanitizeSlideGroupSlug($slug)
	{
	/*
		Sanitize a slide group slug, for accessing the wp_option row with that slug name.

		A wp_option name cannot be greater than 64 chars, so we truncate after 42 chars (63 - length of our option prefix),
		so as not to request a too-long wp_option name from MySQL.

		The create routine will handle if there is an existig name conflict due to the truncation.

	*/
		return substr(preg_replace('/[^a-zA-Z0-9_\-]/', '', $slug), 0, (63 - strlen('total_slider_slides_') ) );
	}

	private function getCurrentSlides($slug) {
	/*
		Returns an array of the current slides in the database, in their
		current precedence order.
	*/
		return get_option('total_slider_slides_' . Total_Slider::sanitizeSlideGroupSlug($slug) );
	}

	private function idFilter($idToFilter)
	{
	/*
		Filter a uniqid string for output to the admin interface HTML.
	*/

		return preg_replace('[^0-9a-zA-Z_]', '', $idToFilter);

	}

	public function uglyJSRedirect($location, $data = false)
	{
	/*
		Redirect, from within the admin panel for this plugin back to the plugin's main page.
	*/

		switch ($location) {

			case 'root':
				$url = 'admin.php?page=total-slider';
			break;

			case 'edit-slide-group':
				$url = 'admin.php?page=total-slider&group=';
				$url .= esc_attr(Total_Slider::sanitizeSlideGroupSlug($data));
			break;

			default:
				$url = 'admin.php?page=total-slider';
			break;

		}

		// erm, just a little bit of an ugly hack :(

		?><script type="text/javascript">window.location.replace('<?php echo $url; ?>');</script>
		<noscript><h1><a href="<?php echo esc_url($url); ?>"><?php _e('Please visit this page to continue', 'total_slider');?></a></h1></noscript><?php
		die();

	}

	public function determineTemplateOptions()
	{
	/*
		Using the active Slider template, determine the desired crop height
		and crop width for the background image, as well as other options, including
		disabling X/Y positioning in admin.

		Requires that custom theme PHP include something like the following:
			/*
			Template Options

			Crop-Suggested-Width: 600
			Crop-Suggested-Height: 300
			Disable-XY-Positioning-In-Admin: No
			*/
		/*

		These are parsed as configuration directives for the admin-side.

	*/

		static $templateOptions = array(); // save for caching

		if (isset($templateOptions) && is_array($templateOptions) && count($templateOptions) > 0)
		{
			// cache results
			return $templateOptions;
		}

		$themePath = get_stylesheet_directory();

		if (@file_exists($themePath . '/total-slider-templates/total-slider-template.css'))
		{
			$tpl = @file_get_contents($themePath . '/total-slider-templates/total-slider-template.php');
		}
		else {
			$tpl = @file_get_contents(dirname(__FILE__) . '/templates/total-slider-template.php');
		}

		if ($tpl !== false)
		{
			// look for Crop-Suggested-Width: xx directive
			$matches = array();
			preg_match('/^\s*Crop\-Suggested\-Width:\s*([0-9]+)/im', $tpl, $matches);
			if (count($matches) == 2)
			{
				if (intval($matches[1]) == $matches[1])
				{
					$cropWidth = intval( $matches[1] );
				}
				else {
					$cropWidth = TOTAL_SLIDER_DEFAULT_CROP_WIDTH;
				}
			}
			else {
				$cropWidth = TOTAL_SLIDER_DEFAULT_CROP_WIDTH;
			}

			// look for Crop-Suggested-Height: xx directive
			$matches = array();
			preg_match('/^\s*Crop\-Suggested\-Height:\s*([0-9]+)/im', $tpl, $matches);
			if (count($matches) == 2)
			{
				if (intval($matches[1]) == $matches[1])
				{
					$cropHeight = intval( $matches[1] );
				}
				else {
					$cropHeight = TOTAL_SLIDER_DEFAULT_CROP_HEIGHT;
				}
			}
			else {
				$cropHeight = TOTAL_SLIDER_DEFAULT_CROP_HEIGHT;
			}

			// look for Disable-XY-Positioning-In-Admin directive
			$matches = array();
			preg_match('/^\s*Disable\-XY\-Positioning\-In\-Admin:\s*(Yes|No|On|Off|1|0|True|False)/im', $tpl, $matches);
			$affirmativeResponses = array('yes', 'on', '1', 'true');
			//$negativeResponses = array('no', 'off', '0', 'false');

			if (count($matches) == 2)
			{
				if (in_array(strtolower($matches[1]), $affirmativeResponses))
				{
					$disableXY = true;
				}
				else {
					$disableXY = false;
				}
			}
			else {
				$disableXY = false;
			}

		}
		else {
			$cropWidth = TOTAL_SLIDER_DEFAULT_CROP_WIDTH;
			$cropHeight = TOTAL_SLIDER_DEFAULT_CROP_HEIGHT;
			$disableXY = false;
		}

		// cache results in global $templateOptions
		$templateOptions = array('crop_width' => $cropWidth, 'crop_height' => $cropHeight, 'disable_xy' => $disableXY);
		return $templateOptions;

	}

	public function setCapabilityForRoles($rolesToSet)
	{
	/*
		Set the TOTAL_SLIDER_REQUIRED_CAPABILITY capability against this role, so this WordPress
		user role is able to manage the slides.

		Will clear out the capability from all roles, then add it to both administrator and the
		specified roles. (Administrator always has access).
	*/
		global $wp_roles;

		if (!current_user_can('manage_options'))
		{
			return false;
		}

		$allRoles = get_editable_roles();
		$validRoles = array_keys($allRoles);

		if (!is_array($allRoles) || count($allRoles) < 1)
		{
			return false;
		}

		// clear the capability from all roles first
		foreach ($allRoles as $rName => $r)
		{
			$wp_roles->remove_cap($rName, TOTAL_SLIDER_REQUIRED_CAPABILITY);
		}

		// add the capability to 'administrator', which can always manage slides
		$wp_roles->add_cap('administrator', TOTAL_SLIDER_REQUIRED_CAPABILITY);

		// add the capability to the specified $roleToSet
		if (is_array($rolesToSet) && count($rolesToSet) > 0)
		{
			foreach($rolesToSet as $theRole)
			{
				if (in_array($theRole, $validRoles))
				{
					$wp_roles->add_cap($theRole, TOTAL_SLIDER_REQUIRED_CAPABILITY);
				}
			}
		}

		return true;

	}

	/***********	Control passing, runtime UI setup, enqueuing etc.	***********/

	public function passControlToAjaxHandler()
	{
	/*
		If the user is trying to perform an Ajax action, immediately pass
		control over to ajax_interface.php.

		This should hook admin_init() (therefore be as light as possible).
	*/

		if (
			array_key_exists('page', $_GET) && $_GET['page'] == 'total-slider' &&
			array_key_exists('total-slider-ajax', $_GET) && $_GET['total-slider-ajax'] == 'true'
		)
		{
			require_once(dirname(__FILE__).'/ajax_interface.php');
		}

	}

	public function addAdminSubMenu() {
	/*
		Add the submenu to the admin sidebar for the configuration screen.
	*/
		if ( array_key_exists( 'page', $_GET ) && $_GET['page'] == 'total-slider' )
		{

			// get our JavaScript on
			wp_enqueue_script('jquery');
			wp_enqueue_script('jquery-ui');

			wp_enqueue_script('wp-ajax-response');

			wp_enqueue_script('media');
			wp_enqueue_script('media-upload');
			wp_enqueue_script('thickbox');
			wp_enqueue_style('thickbox');

			wp_enqueue_script('postbox');

			wp_enqueue_script('jquery-ui-draggable');
			wp_enqueue_script('jquery-ui-droppable');
			wp_enqueue_script('jquery-ui-sortable');

			wp_enqueue_style( 'wp-pointer' );
			wp_enqueue_script( 'wp-pointer' );

			wp_register_script('total-slider-interface', plugin_dir_url( __FILE__ ).'js/interface.js');
			wp_enqueue_script('total-slider-interface');

			wp_localize_script('total-slider-interface', '_total_slider_L10n', Total_Slider::jsL10n());


			// enqueue the frontend so that the interface will be ready
			Total_Slider::enqueueSliderFrontend('backend');

			wp_register_style('total-slider-interface-styles', plugin_dir_url( __FILE__ ).'css/interface.css');
			wp_enqueue_style('total-slider-interface-styles');

			// load the WP_Pointer if we are on the Slides page
			if (array_key_exists('group', $_GET))
			{
				add_action( 'admin_print_footer_scripts', array('Total_Slider', 'printHelpPointerJS') );
			}

		}

		/* Top-level menu page */
		add_menu_page(

			__('Slider', 'total_slider'),						/* title of options page */
			__('Slider', 'total_slider'),						/* title of options menu item */
			TOTAL_SLIDER_REQUIRED_CAPABILITY,					/* permissions level */
			'total-slider',									/* menu slug */
			array('Total_Slider', 'printSlideGroupsPage'),		/* callback to print the page to output */
			plugin_dir_url( __FILE__ ).'img/total-slider-icon-16.png',/* icon file */
			null 											/* menu position number */
		);

		/* First child, 'Slide Groups' */
		$submenu = add_submenu_page(

			'total-slider',									/* parent slug */
			__('Slide Groups', 'total_slider'),				/* title of page */
			__('Slide Groups', 'total_slider'),				/* title to use in menu */
			TOTAL_SLIDER_REQUIRED_CAPABILITY,					/* permissions level */
			'total-slider',									/* menu slug */
			array('Total_Slider', 'printSlideGroupsPage')		/* callback to print the page to output */

		);

		/* 'Settings' */
		add_submenu_page(

			'total-slider',									/* parent slug */
			__('Settings', 'total_slider'),					/* title of page */
			__('Settings', 'total_slider'),					/* title to use in menu */
			TOTAL_SLIDER_REQUIRED_CAPABILITY,					/* permissions level */
			'total-slider-settings',							/* menu slug */
			array('Total_Slider', 'printSettingsPage')			/* callback to print the page to output */

		);

		add_action( 'admin_head-'. $submenu, array('Total_Slider', 'addSlidesHelp') );


	}

	public function printHelpPointerJS()
	{
	/*
		Print the help pointer JavaScript.
	*/

		// if dismissed, set empty pointer content, which hides it
		$dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );

		if ( in_array( 'total-slider-help-pointer', $dismissed ) ) {
			$pointer_content = '';
		}
		else {
			$pointer_content = '<h3>'.esc_attr ( __('Need help?', 'total_slider') ).'</h3>';
		    $pointer_content .= '<p>'.esc_attr ( __('The help menu will walk you through creating new groups, adding slides, and getting them to display in your theme. It’s a great place to start!', 'total_slider') ) . '</p>';
	    }
	?>
	<script type="text/javascript">
	/*<![CDATA[ */
	jQuery(document).ready( function($) {
		$('#contextual-help-link-wrap').pointer({
			content: '<?php echo $pointer_content; ?>',
			position: {
				edge:  'top',
				align: 'right'
			},
			pointerClass: 'slider-help-pointer',
			close: function() {
				$.post( ajaxurl, {
						pointer: 'total-slider-help-pointer',
						_ajax_nonce: $('#_ajax_nonce').val(),
						action: 'dismiss-wp-pointer'
				});
			}
		}).pointer('open');

		$(window).resize(function() {
			if ( $('.slider-help-pointer').is(":visible") ) $('#contextual-help-link-wrap').pointer('reposition');
		});

		$('#contextual-help-link-wrap').click( function () {
			setTimeout( function () {
				$('#contextual-help-link-wrap').pointer('close');
			}, 0);
		});
	});
	//]]>
	</script>
	<style>
	.slider-help-pointer .wp-pointer-arrow {
		right:10px;
		left:auto;
	}
</style><?php

	}

	public function enqueueSliderFrontend($context = 'frontend')
	{
	/*
		When WordPress is enqueueing the styles, inject our slider CSS and JavaScript in.
		Use the default template if not available in the active theme, or use the active theme's
		Total Slider templates if they do indeed exist.

		If $context is 'backend', we will load the CSS only and not the JS.

	*/

		$generalOptions = get_option('total_slider_general_options');

		// do not run if enqueue is disabled
		if ( is_array($generalOptions) && array_key_exists('should_enqueue_template', $generalOptions)
		&& $generalOptions['should_enqueue_template'] == '0' && $context != 'backend'  )
		{
			return false;
		}

		// look for a template file for total-slider in the current active theme
		$themePath = get_stylesheet_directory();

		if (
			@file_exists($themePath . '/total-slider-templates' )
			&& @is_dir($themePath . '/total-slider-templates' )
			&& @file_exists($themePath . '/total-slider-templates/total-slider-template.css')
		)
		{

			// determine theme URL
			$themeURL = get_stylesheet_directory_uri(); // get 'stylesheet' not 'template' will support child themes

			// enqueue the user's custom CSS template
			wp_register_style(
				'total-slider-frontend',																		/* handle */
				$themeURL . '/total-slider-templates/total-slider-template.css',								/* src */
				array(),																					/* deps */
				date("Ymd", @filemtime($themePath . '/total-slider-templates/total-slider-template.css') ) , 	/* ver */
				'all'																						/* media */
			);

			wp_enqueue_style('total-slider-frontend');

			if ($context != 'backend')
			{
				// is there a custom JS to use?
				if ( @file_exists($themePath . '/total-slider-templates/total-slider-template.js') )
				{

					// enqueue the user's custom JS template
					wp_register_script(
						'total-slider-frontend',																		/* handle */
						$themeURL . '/total-slider-templates/total-slider-template.js',									/* src */
						array(),																					/* deps */
						date("Ymd", @filemtime($themePath . '/total-slider-templates/total-slider-template.js') ) , 	/* ver */
						'all'																						/* media */
					);

					wp_enqueue_script('total-slider-frontend');

				}
				else {
					// use the default JS
					wp_register_script(
						'total-slider-jquery-cycle-lite',															/* handle */
						plugin_dir_url( __FILE__ ) . 'js/jquery.cycle.lite.js',									/* src */
						array('jquery'),																		/* deps */
						date("Ymd", @filemtime(plugin_dir_path( __FILE__ ) .
														 '/js/jquery.cycle.lite.js')) , 						/* ver */
						'all'
					);


					wp_enqueue_script('total-slider-jquery-cycle-lite');


					// and the frontend
					wp_register_script(
						'total-slider-frontend',																/* handle */
						plugin_dir_url( __FILE__ ) . 'templates/total-slider-template.js',					/* src */
						array('jquery', 'total-slider-jquery-cycle-lite'),									/* deps */
						date("Ymd", @filemtime(plugin_dir_path( __FILE__ ) .
														 '/templates/total-slider-template.js')) , 			/* ver */
						'all'
					); wp_enqueue_script('total-slider-frontend'); // fixes #6
				}
			}

		}
		else {

			// enqueue our defaults
			wp_register_style(
				'total-slider-frontend',																		/* handle */
				plugin_dir_url( __FILE__ ) . 'templates/total-slider-template.css',							/* src */
				array(),																					/* deps */
				date("Ymd", @filemtime(plugin_dir_path( __FILE__ ) .
													 '/templates/total-slider-template.css')) , 				/* ver */
				'all'																						/* media */
			);

			wp_enqueue_style('total-slider-frontend');

			if ($context != 'backend')
			{
				// also bring in the jquery cycle lite

				wp_register_script(
					'total-slider-jquery-cycle-lite',															/* handle */
					plugin_dir_url( __FILE__ ) . 'js/jquery.cycle.lite.js',									/* src */
					array('jquery'),																		/* deps */
					date("Ymd", @filemtime(plugin_dir_path( __FILE__ ) .
													 '/js/jquery.cycle.lite.js')) , 						/* ver */
					'all'
				);

				wp_enqueue_script('total-slider-jquery-cycle-lite');

				// and the frontend
				wp_register_script(
					'total-slider-frontend',																/* handle */
					plugin_dir_url( __FILE__ ) . 'templates/total-slider-template.js',					/* src */
					array('jquery', 'total-slider-jquery-cycle-lite'),									/* deps */
					date("Ymd", @filemtime(plugin_dir_path( __FILE__ ) .
													 '/templates/total-slider-template.js')) , 			/* ver */
					'all'
				);

				wp_enqueue_script('total-slider-frontend');

			}
		}

	}

	public function addSlidesHelp()
	{
	/*
		Add our help tab to the Slides page.
	*/

		$screen = get_current_screen();

		$slideGroupsHelp[0] = __('Each slide group contains a number of slides that will appear, one after another, when you publish your slides on your site.', 'total_slider');
		$slideGroupsHelp[1] = sprintf(__('You can make up to %d slide groups, which you can use to have different slideshows on different parts of your site.', 'total_slider'), intval(TOTAL_SLIDER_MAX_SLIDE_GROUPS));

		$screen->add_help_tab( array (
			'id'			=>			'total-slider-groups',
			'title'			=>			__('Slide Groups', 'total_slider'),
			'content'		=>			'<p>'.$slideGroupsHelp[0].'</p><p>'.$slideGroupsHelp[1].'</p>'

		) );

		$editingHelp[0] = __('Once you have clicked ‘Edit’ on the desired slide group, you’ll see all of its slides.', 'total_slider');
		$editingHelp[1] = __('Click on any slide to make changes. You can also drag and drop the title and description to place them anywhere over the background image.', 'total_slider');
		$editingHelp[2] = __('Simply drag and drop to re-order the slides within the group. The new order is saved immediately.', 'total_slider');

		$screen->add_help_tab( array (
			'id'			=>			'total-slider-editing',
			'title'			=>			__('Editing', 'total_slider'),
			'content'		=>			'<p>'.$editingHelp[0].'</p><p>'.$editingHelp[1].'</p><p>'.$editingHelp[2].'</p>'

		) );

		$publishingHelp[0] = __('Once you are happy with your new slide group, you need to publish it for it to show up on your site.', 'total_slider');
		$publishingHelp[1] = __('To do this, your theme needs to support Widgets, and have a widget area in the theme where you’d like the slides to show up.', 'total_slider');
		$publishingHelp[2] = __('Go to <a href="widgets.php">Appearance » Widgets</a> and drag a <strong>Total Slider</strong> widget to the desired sidebar. In the widget’s settings, choose the slide group you would like to show and click <em>Save</em>.', 'total_slider');


		$screen->add_help_tab( array(

			'id'			=>			'total-slider-publishing',
			'title'			=>			__('Publishing', 'total_slider'),
			'content'		=>			'<p>'.$publishingHelp[0].'</p><p>'.$publishingHelp[1].'</p><p>'.$publishingHelp[2].'</p>'

		) );

		$crop = Total_Slider::determineTemplateOptions();

		$hintsTips[0] = sprintf( __('For the best visual results, crop your background images to the size used by your slide template — %d×%d', 'total_slider'), $crop['crop_width'], $crop['crop_height'] );
		$hintsTips[1] = __('Experiment with dragging and dropping the title and description over different parts of the background to achieve a different visual effect.', 'total_slider');
		$hintsTips[2] = __('Keep your site fresh — create multiple slide groups ahead of time, then simply edit the <strong>Total Slider</strong> widget to switch over to display another slide group every now and then.', 'total_slider');
		$hintsTips[3] = __('Completely customise the look of your slides — create a <em>total-slider-templates</em> subfolder in your theme. You can use our <em>templates</em> folder in the plugin as a starting point.', 'total_slider');

		$screen->add_help_tab( array(

			'id'			=>			'total-slider-hints',
			'title'			=>			__('Hints &amp; Tips', 'total_slider'),
			'content'		=>			'<ul><li>'.$hintsTips[0].'</li><li>'.$hintsTips[1].'</li><li>'.$hintsTips[2].'</li><li>'.$hintsTips[3].'</li></ul>'

		) );


	}

	public function jsL10n()
	{
	/*
		Return the object containing all of the i18n-capable and l10n-capable
		strings in the interface.js file, ready for wp_localize_script.
	*/

		return array (

			'switchEditWouldLoseChanges'	=> __("You are still editing the current slide. Switching to a different slide will lose your changes.\n\nDo you want to lose your changes?", 'total_slider'),
			'leavePageWouldLoseChanges'		=> __('You are still editing the current slide. Leaving this page will lose your changes.', 'total_slider'),
				/* note:
					This message may or may not be shown. This is browser-dependent. All we can do in some cases is throw a generic
					“don’t leave, you haven’t saved yet” confirm box, which is better than nothing.
				*/
			'wouldLoseUnsavedChanges'		=> __("You will lose any unsaved changes.\n\nAre you sure you want to lose these changes?", 'total_slider'),
			'confirmDeleteOperation'		=> __("Are you sure you want to delete this slide?\n\nThis action cannot be undone.", 'total_slider'),
			'validationErrorIntroduction'	=> __("Please correct the following errors with the form.\n\n", 'total_slider'),
			'validationNoSlideTitle'		=> __('You must enter a slide title.', 'total_slider'),
			'validationNoSlideDescription'	=> __('You must enter a slide description.', 'total_slider'),
			'validationInvalidBackgroundURL'=> __('The supplied background image URL is not a valid URL.', 'total_slider'),
			'validationInvalidLinkURL'		=> __('The supplied external link is not a valid URL.', 'total_slider'),
			'validationInvalidLinkID'		=> __('The supplied post ID for the slide link is not valid.', 'total_slider'),
			'sortWillSaveSoon'				=> __('The new order will be saved when you save the new slide.', 'total_slider'),
			'unableToResortSlides'			=> __('Sorry, unable to resort the slides.', 'total_slider'),
			'newSlideTemplateUntitled'		=> __('untitled', 'total_slider'),
			'newSlideTemplateNoText'		=> __('(no text)', 'total_slider'),
			'newSlideTemplateMove'			=> __('Move', 'total_slider'),
			'newSlideTemplateDelete'		=> __('Delete', 'total_slider'),
			'slideEditNoPostSelected'		=> __('No post selected.', 'total_slider'),
			'saveButtonValue'				=> __('Save', 'total_slider'),
			'unableToGetSlide'				=> __('Sorry, unable to get that slide', 'total_slider'),
			'slideSaved'					=> __('Slide saved.', 'total_slider'),
			'uploadSlideBgImage'			=> __('Upload slide background image', 'total_slider'),
			'unableToSaveSlide'				=> __('Sorry, unable to save the new slide.', 'total_slider'),
			'unableToDeleteSlideNoID'		=> __('Unable to delete -- could not get the slide ID for the current slide.', 'total_slider'),
			'unableToDeleteSlide'			=> __('Sorry, unable to delete the slide.', 'total_slider')

		);

	}

	/***********	Print functions for each plugin admin page	***********/

	public function printSlideGroupsPage()
	{
	/*
		Print the page for adding, deleting Slide Groups and for pushing people over
		to the 'actual' slides editing interface for that Slide Group.
	*/


		// permissions check
		if (!current_user_can(TOTAL_SLIDER_REQUIRED_CAPABILITY))
		{
			?><h1>This page is not accessible to your user.</h1><?php
			return;
		}

		// add the credits/notes metabox
		add_meta_box('credits-notes', __('Credits', 'total_slider'), array('Total_Slider', 'printCreditsMetabox'), '_total_slider_slide_groups', 'side', 'core');

		// if we are to remove a slide group, do that and redirect to home
		if (array_key_exists('action', $_GET) && $_GET['action'] == 'remove' && array_key_exists('group', $_GET))
		{
			if (wp_verify_nonce($_REQUEST['_wpnonce'], 'remove-slide-group'))
			{
				// remove the slide group
				$newGroup = new Total_Slide_Group($_GET['group']);
				$newGroup->delete();

				// remove the option
				delete_option('total_slider_slides_'. Total_Slider::sanitizeSlideGroupSlug($_GET['group']));

				// redirect back to the admin total slider root page
				Total_Slider::uglyJSRedirect('root');
				die();

			}
		}

		// if we are to batch remove slide groups, do so and redirect to home
		if (array_key_exists('slidegroup', $_POST) && ( array_key_exists('action', $_POST) || array_key_exists('action2', $_POST) )
			&& array_key_exists('_wpnonce', $_POST)	&& ( $_POST['action'] == 'remove' || $_POST['action2'] == 'remove' )
			&& is_array($_POST['slidegroup']) && count($_POST['slidegroup'] > 0)
		)
		{
			if (wp_verify_nonce($_POST['_bulk_wpnonce'], 'remove-bulk-slide-group'))
			{
				// remove selected slide groups
				foreach($_POST['slidegroup'] as $slideGroup)
				{
					// remove the slide group
					$newGroup = new Total_Slide_Group($slideGroup);
					$newGroup->delete();

					// remove the option
					delete_option('total_slider_slides_'. Total_Slider::sanitizeSlideGroupSlug($slideGroup));
				}

				// redirect back to the admin total slider root page
				Total_Slider::uglyJSRedirect('root');
				die();
			}
		}

		// if the URL otherwise has 'group' in the GET parameters, it's time to pass control
		// to printSlidesPage() for editing purposes
		if (array_key_exists('group', $_GET))
		{
			Total_Slider::printSlidesPage();
			return;
		}

		// if we are to create a new slide group, do that and redirect to edit
		if (array_key_exists('action', $_GET) && $_GET['action'] == 'new_slide_group')
		{
			if (wp_verify_nonce($_REQUEST['_wpnonce'], 'new-slide-group'))
			{

				if (!empty($_POST['group-name']))
				{
					// add the new slide group
					$newSlug = Total_Slider::sanitizeSlideGroupSlug(sanitize_title_with_dashes($_POST['group-name']));

					// slide group already with this name?
					$existing = new Total_Slide_Group($newSlug);
					if ($existing->load())
					{
						$newSlug = substr($newSlug, 0, (63 - strlen('total_slider_slides_') - 23 ) ); // truncate so that the uniqid portion is retained.
						$newSlug .= Total_Slider::idFilter(uniqid('_', true));
						$newSlug = Total_Slider::sanitizeSlideGroupSlug($newSlug);
					}

					if (empty($newSlug))
					{
						$newSlug = 'group_' . Total_Slider::idFilter(uniqid('', true));
						$newSlug = Total_Slider::sanitizeSlideGroupSlug($newSlug);
					}

					$newGroup = new Total_Slide_Group($newSlug, $_POST['group-name']);
					$newGroup->save();

					// add the new slides option for this group
					add_option('total_slider_slides_'.$newSlug, array(), '', 'yes');

					// redirect to the new edit page for this slide group
					Total_Slider::uglyJSRedirect('edit-slide-group', $newSlug);
					die();
				}
			}
		}

		?>
		<script type="text/javascript">
		//<![CDATA[
		jQuery(document).ready(function() {
			jQuery('#new-slide-group-button').click(function(e) {
				e.preventDefault();
				jQuery('#new-slide-group').show('slow');
			});
			jQuery('#new-slide-group-cancel').click(function(e) {
				e.preventDefault();
				jQuery('#new-slide-group').hide('slow');
			});
		});
		var VPM_SHOULD_WORKAROUND_16655 = false; // irrelevant here, but must be defined
		var VPM_SHOULD_DISABLE_XY = false
		//]]>
		</script>
		<div class="wrap">

		<div id="icon-total-slides" class="icon32"><br /></div><h2><?php _e('Slide Groups', 'total_slider');?> <a href="#" id="new-slide-group-button" class="add-new-h2"><?php _e('Add New', 'total_slider');?></a></h2>

		<noscript>
		<h3><?php _e('Sorry, this interface requires JavaScript to function.', 'total_slider');?></h3>
		<p><?php _e('You will need to enable JavaScript for this page before many of the controls below will work.', 'total_slider');?></p>
		</noscript>

		<div id="new-slide-group">
			<form name="new-slide-group-form" id="new-slide-group-form" method="post" action="admin.php?page=total-slider&action=new_slide_group">
				<h3 id="new-slide-group-header"><?php _e('Add a Slide Group', 'total_slider');?></h3>
				<?php wp_nonce_field('new-slide-group');?>
				<table class="form-table" style="max-width:690px">

					<tr class="form-field form-required">
						<th scope="row"><label for="group-name"><?php _e('Group Name', 'total_slider');?></label></th>
						<td><input name="group-name" type="text" id="group-name" value="" /></td>
					</tr>
				</table>
				<p class="submit"><input type="submit" class="button-primary" value="<?php _e('Add Slide Group', 'total_slider');?>"  />
				<input type="button" id="new-slide-group-cancel" class="button-secondary" value="<?php _e('Cancel', 'total_slider');?>" /></p></form>
			</form>
		</div>

		<div id="poststuff">
			<div class="metabox-holder has-right-sidebar columns-2">

				<div class="inner-sidebar" id="postbox-container-1">
					<?php do_meta_boxes('_total_slider_slide_groups', 'side', null);?>
				</div>

				<div id="post-body" class="columns-2"><div id="post-body-content">
				<form id="slide-groups-bulk-actions" method="post" action="admin.php?page=total-slider" onsubmit="if (jQuery('.slide-group-checkbox:checked').length > 0 && jQuery('option[value=remove]:selected').length > 0) { return confirm('<?php _e('Are you sure you want to delete these slide groups?\n\nThis action cannot be undone.', 'total_slider');?>'); }">
					<input type="hidden" name="_bulk_wpnonce" value="<?php echo wp_create_nonce('remove-bulk-slide-group');?>" />
					<?php require_once( dirname( __FILE__ ) . '/slide_groups_table.php');
					$table = new Slide_Groups_Table();
					$table->prepare_items();
					$table->display();
					?>
				</form>
				</div></div>

			</div>
		</div>

		</div><!--wrap-->
		<?php
	}

	public function printSlidesPage() {
	/*
		Print the actual slides page for adding, editing and removing the slides.
	*/

		global $theSlug;

		// permissions check
		if (!current_user_can(TOTAL_SLIDER_REQUIRED_CAPABILITY))
		{
			?><h1><?php _e('This page is not accessible to your user.', 'total_slider');?></h1><?php
			return;
		}

		$theSlug = Total_Slider::sanitizeSlideGroupSlug($_GET['group']);
		if (empty($theSlug))
		{
			echo '<div class="wrap"><h1>';
			_e('No Slide Group selected.', 'total_slider');
			echo '</h1></div>';
			return;
		}

		// get the name data for this slide group based on its slug
		$slideGroup = new Total_Slide_Group($_GET['group']);
		if (!$slideGroup->load())
		{
			echo '<div class="wrap"><h1>';
			_e('Could not load the selected Slide Group. Does it exist?', 'total_slider');
			echo '</h1></div>';
			return;
		}


		// add the metaboxes
		add_meta_box('slide-sorter-mb', __('Slides', 'total_slider'), array('Total_Slider', 'printSlideSorterMetabox'), '_total_slider_slide', 'normal', 'core');
		add_meta_box('slide-preview-mb', __('Preview', 'total_slider'), array('Total_Slider', 'printSlidePreviewMetabox'), '_total_slider_slide', 'normal', 'core');

		add_meta_box('slide-editor-mb', __('Edit', 'total_slider'), array('Total_Slider', 'printSlideEditorMetabox'), '_total_slider_slide_bottom', 'normal', 'core');
		add_meta_box('credits-notes-mb', __('Credits', 'total_slider'), array('Total_Slider', 'printCreditsMetabox'), '_total_slider_slide_bottom', 'side', 'core');

		if (function_exists('find_posts_div')) { find_posts_div(); } // bring in the post/page finder interface for links

		?>

		<script type="text/javascript">
		//<![CDATA[
		var VPM_WP_ROOT = '<?php echo admin_url(); ?>';
		var VPM_HPS_PLUGIN_URL = '<?php echo admin_url();?>admin.php?page=total-slider&total-slider-ajax=true&';
		var VPM_HPS_GROUP = '<?php echo esc_attr($theSlug);?>';
		document.title = '‘<?php echo esc_attr($slideGroup->name);?>’ Slides ' + document.title.substring(13, document.title.length);//TODO i18n
		var VPM_SHOULD_WORKAROUND_16655 = <?php echo (version_compare(get_bloginfo('version'), '3.4', '>=') ? 'false' : 'true');?>;
		// on WordPress version <3.4, we need to work around https://core.trac.wordpress.org/ticket/16655. It is fixed in 3.4.
		<?php $templateOptions = Total_Slider::determineTemplateOptions(); ?>

		var VPM_SHOULD_DISABLE_XY = <?php echo ($templateOptions['disable_xy']) ? 'true' : 'false';?>;
		//]]>
		</script>

		<div class="wrap">

		<div id="icon-total-slides" class="icon32"><br /></div>
		<h2><?php printf(__('‘%s’ Slides', 'total_slider'), esc_html($slideGroup->name));?>
		<a href="#" id="new-slide-button" class="add-new-h2"><?php _e('Add New', 'total_slider');?></a></h2>

		<noscript>
		<h3><?php _e('Sorry, this interface requires JavaScript to function.', 'total_slider');?></h3>
		<p><?php _e('You will need to enable JavaScript for this page before any of the controls below will work.', 'total_slider');?></p>
		</noscript>

		<form name="vpm-the-slides">

			<div id="message-area" class="updated settings-error below-h2"></div>
			<div id="poststuff">
				<div class="metabox-holder">
					<?php do_meta_boxes('_total_slider_slide', 'normal', null);?>
				</div>

				<div class="metabox-holder has-right-sidebar columns-2">
					<div class="inner-sidebar" id="postbox-container-1">
						<?php do_meta_boxes('_total_slider_slide_bottom', 'side', null);?>
					</div>
					<div id="post-body" class="columns-2">
						<div id="post-body-content">
							<?php do_meta_boxes('_total_slider_slide_bottom', 'normal', null);?>
						</div>
					</div>
				</div>
			</div>
		</form>
		</div><?php

	}



	public function printSettingsPage()
	{
	/*
		Print the settings page to output, and also handle any of the Settings forms if they
		have come back to us.
	*/

		if (!current_user_can(TOTAL_SLIDER_REQUIRED_CAPABILITY))
		{
			echo '<h1>';
			_e('You do not have permission to manage Slider settings.', 'total_slider');
			echo '</h1>';
			die();
		}

		$success = null;
		$message = '';

		$otherOptions = get_option('total_slider_general_options');

		if (strtolower($_SERVER['REQUEST_METHOD']) == 'post' && array_key_exists('total-slider-settings-submitted', $_POST))
		{
			// handle the submitted form

			if (!wp_verify_nonce($_POST['_wpnonce'], 'total-slider-settings')) {
				die(__('Unable to confirm the form’s security.', 'total_slider'));
			}

			if (current_user_can('manage_options'))
			{

				$rolesToAdd = array();

				// find any checked roles to add our capability to
				foreach($_POST as $pk => $po)
				{
					if (preg_match('/^required_capability_/', $pk))
					{
						$roleNameChopped = substr($pk, strlen('required_capability_'));

						// do not allow administrator to be modified
						if ($roleNameChopped != 'administrator' && $po == '1')
						{
							$rolesToAdd[] = $roleNameChopped;
						}
					}
				}

				Total_Slider::setCapabilityForRoles($rolesToAdd);
				$success = true;
				$message .= __('Required role level saved.', 'total_slider') . ' ';

			}

			if ( array_key_exists('should_enqueue_template', $_POST) && $_POST['should_enqueue_template'] == '1' )
			{

				if (array_key_exists('should_enqueue_template', $otherOptions) && $otherOptions['should_enqueue_template'] != '1')
				{
					$success = true;
					$message .= __('Advanced settings saved.', 'total_slider') . ' ';
				}

				$otherOptions['should_enqueue_template'] = '1';
				update_option('total_slider_general_options', $otherOptions);
			}
			else {
				// disable the option
				if (array_key_exists('should_enqueue_template', $otherOptions) && $otherOptions['should_enqueue_template'] != '0')
				{
					$success = true;
					$message .= __('Advanced settings saved.', 'total_slider') . ' ';
				}

				$otherOptions['should_enqueue_template'] = '0';
				update_option('total_slider_general_options', $otherOptions);

			}


		}

		?><div class="wrap">
		<div id="icon-total-slides" class="icon32" style="background:transparent url(<?php echo plugin_dir_url( __FILE__ );?>img/total-slider-icon-32.png?ver=20120229) no-repeat;"><br /></div><h2><?php _e('Settings', 'total_slider');?></h2>


		<?php if ($success): ?>
			<div class="updated settings-error">
				<p><strong><?php echo esc_html($message); ?></strong></p>
			</div>
		<?php endif; ?>


		<form method="post" action="admin.php?page=total-slider-settings">
			<input type="hidden" name="total-slider-settings-submitted" value="true" />
			<?php wp_nonce_field ( 'total-slider-settings' ); ?>

			<!-- Only display 'Required Role Level' to manage_options capable users -->
			<?php if (current_user_can('manage_options')):?>

			<table class="form-table edit-controls-form-table">
				<tbody>
					<tr class="form-field">
						<th scope="row">
							<label for="required_capabilities"><?php _e('Required role level', 'total_slider');?></label>
						</th>
						<td>
							<?php
							$allRoles = get_editable_roles();
							?>

							<?php
									if (is_array($allRoles) && count($allRoles) > 0):
										foreach($allRoles as $rName => $r): ?>
								<label for="required_capability_<?php echo esc_attr($rName);?>">
										<input type="checkbox" name="required_capability_<?php echo esc_attr($rName);?>"
										id="required_capability_<?php echo esc_attr($rName);?>" value="1" style="width:20px;"
											<?php
											/* if this role has the total_slider_manage_slides capability, mark it as selected */

											if (array_key_exists(TOTAL_SLIDER_REQUIRED_CAPABILITY, $r['capabilities'])): ?>
											checked="checked"
											<?php endif;?>

											<?php // lock administrator checkbox on
											if ($rName == 'administrator'):
											 ?>
											disabled="disabled"
											 <?php endif; ?>

										 /><?php echo esc_html($r['name']);?>
									</label><br/>
							<?php endforeach; endif; ?>
							<span class="description"><?php _e('Users belonging to checked roles will be able to create, edit and delete slides. Only users that can manage widgets are able to activate, deactivate or move the Total Slider widget, which makes the slides show up on your site.', 'total_slider');?></span>
						</td>
					</tr>

				</tbody>
			</table>

			<h3><?php _e('Advanced Settings', 'total_slider');?></h3>

			<table class="form-table edit-controls-form-table">
				<tbody>
					<tr class="form-field">
						<th scope="row">
							<label for="should_enqueue_template"><?php _e('Load JS & CSS', 'total_slider');?></label>
						</th>
						<td>
							<label for="should_enqueue_template">
								<input type="checkbox" name="should_enqueue_template" id="should_enqueue_template" value="1" style="width:20px;"
								<?php echo ( intval($otherOptions['should_enqueue_template']) ) ? ' checked="checked"' : ''; ?>
								/><?php _e('Automatically load slide template CSS and JavaScript into my theme', 'total_slider');?>
							</label><br/>
							<span class="description"><?php _e('Uncheck for manual control over how slide template CSS and JavaScript are included in your theme.', 'total_slider');?></span>
						</td>
					</tr>
				</tbody>
			</table>

			<?php endif; ?>

		<p class="submit">
			<input class="button-primary" type="submit" value="<?php _e('Save Changes', 'total_slider');?>" id="submitbutton" />
		</p>

		</form>
		</div><?php

	}

	public function printUploaderJavaScript()
	{
	/*
		Print the JavaScript to inject into the Media Uploader
	*/

		if (array_key_exists('total-slider-uploader', $_GET) && $_GET['total-slider-uploader'] == 'bgimage')
		{

			$crop = Total_Slider::determineTemplateOptions();

		?>
		<!-- a little shimming to prettify the uploader/media library options for Total Slider purposes -->
		<style type="text/css">
		#media-items .post_title,#media-items .image_alt,#media-items .post_excerpt,#media-items .post_content, #media-items .url, #media-items .align { display:none !important; }
		</style>
		<script type="text/javascript">
		//<![CDATA[
		jQuery(document).ready(function() {
			jQuery('.imgedit-response').append('<p style="text-align:center;font-size:12px;color:#909090;"><?php printf(__('Choose ‘Edit Image’ and crop to %d×%d for best results.', 'total_slider'), $crop['crop_width'], $crop['crop_height']);?></p>');

			jQuery('.savesend .button').each(function() {
				jQuery(this).attr('value', '<?php _e('Use as background image', 'total_slider');?>');
			});

			uploader.bind('FileUploaded', function() {
				window.setTimeout(function() {

					jQuery('.imgedit-response').append('<p style="text-align:center;font-size:12px;color:#909090;"><?php printf(__('Choose ‘Edit Image’ and crop to %d×%d for best results.', 'total_slider'), $crop['crop_width'], $crop['crop_height']);?></p>');
					// rename the main action button
					jQuery('.savesend .button').each(function() {
						jQuery(this).attr('value', '<?php _e('Use as background image', 'total_slider');?>');
					});
				}, 680);
			});
		});
		//]]>
		</script>
		<!-- we're done shimming -->
		<?php

		}

	}

	/***********	Metabox printer callbacks	***********/

	public function printCreditsMetabox()
	{
	/*
		Print to output the contents of the credits/notes metabox.
	*/

		?>

		<div id="credits">
			<p><?php _e('Development: <a href="http://peter.upfold.org.uk/">Peter Upfold</a>', 'total_slider');?></p>

			<p><?php _e('Additional UI: <a href="http://www.vanpattenmedia.com/">Chris Van Patten</a>', 'total_slider');?></p>

			<p><?php _e('Icons: <a href="http://www.field2.com/">Ben Dunkle</a>', 'total_slider');?></p>

			<?php if (defined('WPLANG') && WPLANG != '' && strpos(strtolower(WPLANG), 'en') !== 0 ): ?>
				<p>
				<?php
				/* translators: please replace this with a credit to yourself! "Translation: Your Name". This English message will appear when no translation is available, but the user is using WordPress in another language.. */
				?>
				<?php printf(__('<a href="%s">Help us translate this plugin into your language!</a>', 'total_slider'), 'http://www.vanpattenmedia.com/project/total-slider/#translate');?>
				</p>
			<?php endif; ?>
		</div>

		<div id="contrib-note">
			<p><?php printf( __('If you find this plugin useful, or are using it commercially, please consider <a href="%s">donating</a> to support development.', 'total_slider'), 'http://www.vanpattenmedia.com/project/total-slider/#contribute'); ?></p>

			<p><?php printf ( __('You can also <a href="%s">report bugs</a>, <a href="%s">suggest features</a>, or <a href="%s">send pull requests</a>.', 'total_slider'), 'http://www.vanpattenmedia.com/project/total-slider/#contribute', 'http://www.vanpattenmedia.com/project/total-slider/#contribute', 'http://www.vanpattenmedia.com/project/total-slider/#contribute');?></p>

			<p><?php _e('Thanks!', 'total_slider'); ?></p>
		</div>

		<div id="copyright">
			<p><?php _e('© 2011-2012 Peter Upfold. Proud to be <a href="https://www.gnu.org/licenses/gpl-2.0.html">GPLv2 (or later) licensed</a>.', 'total_slider');?></p>
			<p id="vpm-credit"><?php _e('Built by <a href="http://www.vanpattenmedia.com/">Van Patten Media</a>', 'total_slider');?></p>
		</div>
		<?php

	}

	public function printSlideSorterMetabox()
	{
	/*
		Print to output the contents of the slide sorter/slide listing metabox.
	*/

		global $theSlug;

		?><!--sortable slides-->
		<?php $currentSlides = Total_Slider::getCurrentSlides($theSlug); ?>

		<div id="slidesort-container">
		<ul id="slidesort" style="width:<?php echo intval(count($currentSlides)*180); ?>px;">
		<?php

		if (is_array($currentSlides) && count($currentSlides) > 0)
		{

			foreach($currentSlides as $slide) {

				$myId = Total_Slider::idFilter($slide['id']);

				?>

				<li id="slidesort_<?php echo $myId;?>">

					<div class="slidesort_slidebox" style="background: url(<?php echo esc_attr($slide['background']);?>)">
						<div id="slidesort_<?php echo $myId;?>_text" class="slidesort_text"><?php echo stripslashes(esc_html($slide['title']));?></div>

						<a id="slidesort_<?php echo $myId;?>_move_button" class="slidesort-icon slide-move-button" href="#"><?php _e('Move', 'total_slider');?></a>
						<span id="slidesort_<?php echo $myId;?>_delete" class="slide-delete"><a id="slidesort_<?php echo $myId;?>_delete_button" class="slidesort-icon slide-delete-button" href="#"><?php _e('Delete', 'total_slider');?></a></span>
					</div>

				</li>

				<?php

			}

		}

		?>
		</ul>

		<div class="slidesort-add-hint"<?php if (is_array($currentSlides) && count($currentSlides) > 0) echo ' style="display:none"'; ?>>
		<?php _e('Click &lsquo;Add New&rsquo; to create a Slide.', 'total_slider');?></div>

		</div>

		<?php

	}

	public function printSlidePreviewMetabox()
	{
	/*
		Print to output the contents of the slide preview metabox.
	*/
		?>
		<div id="edit-area">

			<ul id="total-slider" class="total-slider">

				<li id="preview-area" class="total-slider-slide">

					<div id="slide-preview" class="total-slider-overlay">
						<h2 class="total-slider-title" id="slide-preview-title"><?php _e('untitled', 'total_slider');?></h2>
						<div class="total-slider-description">
							<p id="slide-preview-description"><?php _e('(no text)', 'total_slider');?></p>
						</div>
					</div>

				</li>

			</ul>

		</div>

		<div style="clear:both;"></div><?php

	}

	public function printSlideEditorMetabox()
	{
	/*
		Print to output the contents of the slide editor metabox.
	*/
	?>

				<div id="edit-controls-choose-hint">
					<p><?php _e('Click a slide to edit it, or click ‘Add New’.', 'total_slider');?></p>
				</div>

				<div id="edit-controls">
				<form id="edit-form">
					<table class="form-table edit-controls-form-table">
						<tbody>
							<tr class="form-field">
								<th scope="row">
									<label for="edit-slide-title"><?php _e('Title', 'total_slider');?></label>
								</th>
								<td>
									<input type="text" name="slide-title" id="edit-slide-title" value="" maxlength="64" class="edit-controls-inputs" />
								</td>
							</tr>
							<tr class="form-field">
								<th scope="row">
									<label for="edit-slide-description"><?php _e('Description', 'total_slider');?></label>
								</th>
								<td>
									<textarea name="slide-description" id="edit-slide-description" class="widefat edit-controls-inputs" rows="4"></textarea>
								</td>
							</tr>

							<tr class="form-field">
								<th scope="row">
									<label for="edit-slide-image-upload"><?php _e('Background', 'total_slider');?></label>
								</th>
								<td>
									<input id="edit-slide-image-url" type="hidden" name="slide-image" />
									<!--<span id="edit-slide-image-title"></span>-->
									<input id="edit-slide-image-upload" type="button" class="button" value="<?php _e('Upload or choose image', 'total_slider');?>" />
								</td>
							</tr>

							<tr class="form-field">
								<th scope="row">
									<?php _e('Slide Link', 'total_slider');?>
								</th>
								<td>
									<label for="slide-link-is-internal">
										<input type="radio" style="width:auto;" name="slide-link-is-internal" id="slide-link-is-internal" value="true" />
									<?php _e('A page or post on this site', 'total_slider');?>
									</label>
									<br>
									<label for="slide-link-is-external">
										<input type="radio" style="width:auto;" name="slide-link-is-internal" id="slide-link-is-external" value="false" />
									<?php _e('An external link', 'total_slider');?>
									</label>
								</td>
							</tr>

							<tr class="form-field" id="slide-link-internal-settings">
								<th scope="row">

								</th>
								<td>
									<span id="slide-link-internal-display"><?php _e('No post selected', 'total_slider');?></span>
									<input id="slide-link-internal-id" name="slide-link-internal" value="" type="hidden" />
									<input id="slide-link-finder" type="button" class="button" value="<?php _e('Find post', 'total_slider');?>" style="width:50px;" />
								</td>
							</tr>

							<tr class="form-field" id="slide-link-external-settings">
								<th scope="row">
								</th>
								<td>
									<input type="text" name="slide-link" id="edit-slide-link" value="" maxlength="255" class="edit-controls-inputs" placeholder="http://www.example.com/" />
								</td>
							</tr>

						</tbody>
					</table>
					<p class="submit">
						<input type="button" id="edit-controls-save" class="button-primary" value="<?php _e('Save', 'total_slider');?>" />
						<input type="button" id="edit-controls-cancel" class="button-secondary" value="<?php _e('Cancel', 'total_slider');?>" />
					</p>
					<div id="edit-controls-saving">
						<img id="edit-controls-spinner" src="images/loading.gif" width="16" height="16" alt="<?php _e('Loading', 'total_slider');?>" />
						<span><?php _e('Saving…', 'total_slider');?></span>
					</div>
				</form>

			</div><?php

	}

	public function printAdminCSS() {
	/*
		Print the admin CSS to show our admin menu icons.
	*/

		?>
		<style type="text/css" id="total-slider-menu-css">
		#toplevel_page_total-slider .wp-menu-image img { visibility: hidden; }
		#toplevel_page_total-slider .wp-menu-image { background: url( <?php echo plugins_url( basename( dirname( __FILE__ ) ) . '/img/slider-icon-switch.png' ) ?> ) 0 90% no-repeat; }
		#toplevel_page_total-slider.current .wp-menu-image, #toplevel_page_total-slider.wp-has-current-submenu .wp-menu-image, #toplevel_page_total-slider:hover .wp-menu-image { background-position: top left; }
		</style>
		<?php

	}


};

/******************************************** Total_Slider_Widget ********************************************/


class Total_Slider_Widget extends WP_Widget {
/*
	The Total Slider Widget is responsible for allowing the user to place the slider in any
	‘sidebar’ defined in their theme and for invoking the Slider template file for displaying
	the slides.

	This widget class also defines a minimalist API for the Slider template files to use to display
	the slides.
*/

	/*
		These hold the data for the current slide we are working with.

		The template file accesses these indirectly, through the the_… and get_the_… functions.
	*/
	private $slides; // stores all of the slides in this group
	private $instance; // has_slides needs access to the instance data
	protected $slide_title;
	protected $slide_description;
	protected $slide_background_url;
	protected $slide_link;
	protected $slide_x;
	protected $slide_y;
	protected $slide_identifier;
	protected $slider_iteration = 0;


	public function __construct(){
	/*
		Constructor, merely calls the WP_Widget constructor.
	*/
		parent::__construct(false, 'Total Slider');
	}

	public function widget($args, $instance) {
	/*
		The widget function is responsible for rendering the widget's output. In the case
		of Total Slider Widget, this will invoke the Slider template file to output the slides
		to the desired widget area.
	*/


		$this->instance = $instance;

		// clear out all the data
		$this->slide_title = null;
		$this->slide_description = null;
		$this->slide_background_url = null;
		$this->slide_link = null;
		$this->slide_x = null;
		$this->slide_y = null;
		$this->slide_identifier = null;
		$this->slides = null;
		$this->slider_iteration = 0;

		$s = &$this; // $s is used by the theme to call our functions to actually display the data

		// look for a template file for total-slider in the current active theme
		$themePath = get_stylesheet_directory();

		if (
			@file_exists($themePath . '/total-slider-templates' )
			&& @is_dir($themePath . '/total-slider-templates' )
			&& @file_exists($themePath . '/total-slider-templates/total-slider-template.php')
		)
		{
			require($themePath . '/total-slider-templates/total-slider-template.php' );
		}
		else
		{ // if not, use our default
			require( dirname(__FILE__) . '/templates/total-slider-template.php' );
		}

		unset($s);

	}

	public function form($instance)
	{
	/*
		The form function defines the settings form for the widget.

		In our case, we will allow the user to pick which Slide Group this widget is responsible
		for displaying.
	*/

	?><p><?php _e('Choose a slide group for this widget to show:', 'total_slider');?></p>

	<select id="<?php echo $this->get_field_id('groupSlug');?>" name="<?php echo $this->get_field_name('groupSlug');?>">
		<option value="**INVALID**" disabled="disabled" selected="selected"><?php _e('Select a group', 'total_slider');?></option>
		<?php

			// find all the slide groups and offer them for the widget

			$slideGroups = get_option('total_slider_slide_groups');

			if (is_array($slideGroups) && count($slideGroups) > 0)
			{
				foreach($slideGroups as $group)
				{
					?><option value="<?php echo esc_attr($group->slug);?>"
						<?php if (array_key_exists('groupSlug', $instance)):
							echo ($group->slug == $instance['groupSlug']) ? ' selected="selected"' : '';
						endif; ?>
					><?php echo esc_html($group->name);?></option><?php
				}

			}

		?>
	</select>
	<?php

	}

	public function update($newInstance, $oldInstance)
	{
	/*
		Update the widget's settings with the new selected slide group from the form()
	*/

		if ($newInstance['groupSlug'] != '**INVALID**')
		{

			return array('groupSlug' => Total_Slider::sanitizeSlideGroupSlug($newInstance['groupSlug']));
		}
		else {
			return false;
		}

	}

	public function slides_count()
	{
	/*
		Return the number of slides in this slide group.

		Can also be used by templates to test if there are any slides to show at all,
		and, for example, not output the starting <ul>.
	*/

		if (!is_array($this->slides))
		{
			$this->slides = get_option('total_slider_slides_' . Total_Slider::sanitizeSlideGroupSlug($this->instance['groupSlug']));
		}

		return count($this->slides);

	}


	public function has_slides()
	{
	/*
		Behaves as an iterator for the purposes of slider template files. It loads
		in the next slide, readying the other functions below for returning
		the data from this particular slide to the theme.


	*/

		if (!$this->instance)
		{
			throw new Exception("The widget's instance data, containing information about which slide group to show, could not be loaded.");
			return false;
		}

		if (!is_array($this->slides) || count($this->slides) < 1)
		{
			$this->slides = get_option('total_slider_slides_' . Total_Slider::sanitizeSlideGroupSlug($this->instance['groupSlug']));
		}

		// on which slide should we work? does it exist?
		if (count($this->slides) < $this->slider_iteration + 1)
		{
			return false; // we are at the end of the slides
		}

		// otherwise, load in the data
		if (!empty ($this->slides[$this->slider_iteration]['title']) )
		{
			$this->slide_title = $this->slides[$this->slider_iteration]['title'];
		}
		if (!empty ($this->slides[$this->slider_iteration]['description']) )
		{
			$this->slide_description = $this->slides[$this->slider_iteration]['description'];
		}

		if (!empty ($this->slides[$this->slider_iteration]['id']) )
		{
			$this->slide_identifier = $this->slides[$this->slider_iteration]['id'];
		}

		// the background may be blank!
		if (!empty ($this->slides[$this->slider_iteration]['background']) )
		{
			$this->slide_background_url = $this->slides[$this->slider_iteration]['background'];
		}
		else {
			$this->slide_background_url = '';
		}

		// the link may be blank!
		if (!empty ($this->slides[$this->slider_iteration]['link']) )
		{
			$this->slide_link = $this->slides[$this->slider_iteration]['link'];
		}
		else {
			$this->slide_link = '';
		}

		// get X and Y coords
		if (!empty ($this->slides[$this->slider_iteration]['title_pos_x']) || $this->slides[$this->slider_iteration]['title_pos_x'] === 0)
		{
			$this->slide_x = $this->slides[$this->slider_iteration]['title_pos_x'];
		}
		if (!empty ($this->slides[$this->slider_iteration]['title_pos_y']) || $this->slides[$this->slider_iteration]['title_pos_y'] === 0 )
		{
			$this->slide_y = $this->slides[$this->slider_iteration]['title_pos_y'];
		}



		// the data is ready, bump the iterator and return true
		$this->slider_iteration++;
		return true;


	}

	public function the_title()
	{
	/*
		Print the slide title to output, having sanitised it.
	*/

		echo $this->get_the_title();

	}

	public function get_the_title()
	{
	/*
		Return the slide title, having sanitised it.
	*/

		return esc_html( apply_filters( 'total-slider_slide_title', $this->slide_title ) );

	}

	public function the_description()
	{
	/*
		Print the slide description to output, having sanitised it.
	*/

		echo $this->get_the_description();

	}

	public function get_the_description()
	{
	/*
		Return the slide description, having sanitised it.
	*/

		return esc_html( apply_filters ( 'total-slider_slide_description', $this->slide_description ) );

	}

	public function the_background_url()
	{
	/*
		Print the background URL to output, having sanitised it.
	*/

		echo $this->get_the_background_url();

	}

	public function get_the_background_url()
	{
	/*
		Return the background URL, having sanitisied it.
	*/

		if (is_numeric($this->slide_background_url))
		{
			$bgAttach = (int)$this->slide_background_url;
			$bgAttach = wp_get_attachment_url($bgAttach);

			return esc_url ( apply_filters('total-slider_slide_background_url', $bgAttach ) );

		}
		else {
			return esc_url( apply_filters ('total-slider_slide_background_url', $this->slide_background_url) );
		}

	}

	public function the_link()
	{
	/*
		Print the slide link URL to output, having sanitised it.
	*/

		echo $this->get_the_link();

	}

	public function get_the_link()
	{
	/*
		Return the slide link URL, having sanitised it.
	*/

		if (is_numeric($this->slide_link))
		{
			$linkPost = (int)$this->slide_link;
			return esc_url( apply_filters('total-slider_slide_link', get_permalink($linkPost) ) );
		}
		else {
			return esc_url ( apply_filters('total-slider_slide_link', $this->slide_link) );
		}

	}

	public function the_x()
	{
	/*
		Print the X coordinate to the output, having sanitised it.
	*/

		echo $this->get_the_x();

	}

	public function get_the_x()
	{
	/*
		Return the X coordinate, having sanitised it.
	*/

		return intval ( apply_filters( 'total-slider_slide_x', $this->slide_x ), 10 /* decimal */ );

	}

	public function the_y()
	{
	/*
		Print the Y coordinate to the output, having sanitised it.
	*/

		echo $this->get_the_y();

	}

	public function get_the_y()
	{
	/*
		Return the Y coordinate, having sanitised it.
	*/

		return intval ( apply_filters( 'total-slider_slide_y', $this->slide_y ), 10 /* decimal */ );

	}

	public function the_identifier()
	{
	/*
		Print the slide identifier to output, having sanitised it.
	*/

		echo $this->get_the_identifier();

	}

	public function get_the_identifier()
	{
	/*
		Return the slide identifier to output, having sanitised it.
	*/

		return esc_attr( apply_filters('total-slider_slide_identifier', $this->slide_identifier) );

	}

	public function iteration()
	{
	/*
		Return the iteration number. How many slides have we been through?
	*/

		return intval ( $this->slider_iteration - 1 );
		// has_slides() always bumps the iteration ready for the next run, but we
		// are still running, for the theme's purposes, on the previous iteration.
		// Hence, returning the iteration - 1.

	}


};

/******************************************** WordPress actions ********************************************/

register_activation_hook(__FILE__, array('Total_Slider', 'createSlidesOptionField'));
add_action('init', array('Total_Slider', 'loadTextDomain'));
add_action('admin_menu', array('Total_Slider', 'addAdminSubMenu'));
add_action( 'admin_head', array('Total_Slider', 'printAdminCSS'));
add_action('widgets_init', array('Total_Slider', 'registerAsWidget'));
add_action('admin_init', array('Total_Slider', 'passControlToAjaxHandler'));
add_action('admin_head-media-upload-popup', array('Total_Slider', 'printUploaderJavaScript'));

add_action('wp_enqueue_scripts', array('Total_Slider', 'enqueueSliderFrontend'));

?>